package main

import (
	"context"
	"fmt"
	"log"
	"strconv"
	"time"

	"github.com/go-redis/redis/v8"
)

type LogService struct {
	Conn *redis.Client
	ctx  context.Context
}

func NewLogService(conn *redis.Client) *LogService {
	return &LogService{Conn: conn, ctx: context.Background()}
}

func (c *LogService) LogRecent(name, message, severity string, pipeline redis.Pipeliner) {
	if severity == "" {
		severity = "INFO"
	}
	destination := fmt.Sprintf("recent:%s:%s", name, severity)
	message = time.Now().Local().String() + " " + message

	if pipeline == nil {
		pipeline = c.Conn.Pipeline()
	}
	pipeline.LPush(c.ctx, destination, message)
	pipeline.LTrim(c.ctx, destination, 0, 99)
	if _, err := pipeline.Exec(c.ctx); err != nil {
		log.Println("LogRecent pipeline err: ", err)
	}
}

func (c *LogService) LogCommon(name, message, severity string, timeout int64) {
	destination := fmt.Sprintf("common:%s:%s", name, severity)
	startKey := destination + ":start"
	end := time.Now().Add(time.Duration(timeout) * time.Millisecond)

	for time.Now().Before(end) {
		err := c.Conn.Watch(c.ctx, func(tx *redis.Tx) error {
			hourStart := time.Now().Local().Hour()
			existing, _ := strconv.Atoi(tx.Get(c.ctx, startKey).Val())

			if _, err := tx.Pipelined(c.ctx, func(pipeline redis.Pipeliner) error {
				if existing != 0 && existing < hourStart {
					pipeline.Rename(c.ctx, destination, destination+":last")
					pipeline.Rename(c.ctx, startKey, destination+":start")
					pipeline.Set(c.ctx, startKey, hourStart, 0)
				} else if existing == 0 {
					pipeline.Set(c.ctx, startKey, hourStart, 0)
				}

				pipeline.ZIncrBy(c.ctx, destination, 1, message)
				c.LogRecent(name, message, severity, pipeline)
				return nil
			}); err != nil {
				log.Println("LogCommon pipelined failed, err: ", err)
				return err
			}
			return nil
		}, startKey)
		if err != nil {
			log.Println("watch failed, err: ", err)
			continue
		}
	}
}
